package com.bc.bcapi.service;

import com.bc.bcapi.dao.*;
import com.bc.bcapi.utils.web3j.Util;
import com.bc.bcmodel.consts.MultiLanguage;
import com.bc.bcmodel.domain.T_Erc_contractModel;
import com.bc.bcmodel.domain.T_User_withdrawModel;
import com.bc.bcmodel.domain.T_Wallet_infoModel;
import com.bc.bcmodel.req.AccountWithdrawReq;
import com.bc.bcmodel.resp.WalletTokenResp;
import com.bc.commoncore.util.CookieUtils;
import com.bc.commoncore.util.StringUtils;
import com.bc.commonentity.common.DBContextDbType;
import com.bc.commonentity.common.DBContextHolder;
import com.bc.commonentity.common.JsonResult;
import com.bc.commonentity.enums.HttpReturnRnums;
import com.bc.commonentity.enums.SeqNameEnum;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.math.BigInteger;

import com.bc.bcmodel.domain.T_User_walletModel;

import com.bc.bccore.web3j.WalletService;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Name T_User_walletService * @Description TODO * @Author generation * @Date 2018/8/14 15:26:24 * @Version 1.0
 **/
@Service
public class T_User_walletService {
    @Resource
    T_User_walletMapper t_User_walletMapper;
    @Resource
    T_User_mytokenMapper t_User_mytokenMapper;
    @Resource
    T_Wallet_infoMapper t_wallet_infoMapper;
    @Resource
    T_SequenceService t_sequenceService;
    @Resource
    T_User_withdrawMapper t_user_withdrawMapper;
    @Resource
    T_Erc_contractMapper t_erc_contractMapper;

    /**
     * 保存单条数据     *      * @return
     */

    public int insert(T_User_walletModel req) {
        return t_User_walletMapper.insert(req);
    }

    /**
     * 批量增加数据     *      * @return
     */

    public int insertBatch(List<T_User_walletModel> req) {
        return t_User_walletMapper.insertBatch(req);
    }

    /**
     * 根据对象属性查找满足条件的第一条数据     *      * @return
     */

    public T_User_walletModel findByNamedParam(T_User_walletModel req) {
        return t_User_walletMapper.findByNamedParam(req);
    }

    /**
     * 根据对象查询所有数据     *      * @return
     */

    public List<T_User_walletModel> getAllList(T_User_walletModel req) {
        return t_User_walletMapper.findByNamedParamList(req);
    }

    /**
     * 根据对象查询所有分页数据     *      * @return
     */

    public PageInfo<T_User_walletModel> getList(T_User_walletModel req) {
        PageHelper.startPage(req.getPage_index(), req.getPage_size());
        List<T_User_walletModel> list = t_User_walletMapper.findByNamedParamList(req);
        return new PageInfo<T_User_walletModel>(list);
    }

    /**
     * 更新指定对象     *      * @return
     */

    public int update(T_User_walletModel req) {
        return t_User_walletMapper.update(req);
    }

    /**
     * 删除指定数据     *      * @return
     */

    public int delete(BigInteger id) {
        return t_User_walletMapper.delete(id);
    }

    /**
     * 根据条件统计数据量     *      * @return
     */

    public long count(T_User_walletModel req) {
        return t_User_walletMapper.count(req);
    }

    public String createWallet(String uid) throws Exception {
        //先查是否存在
        T_User_walletModel pre_t_user_walletModel = t_User_walletMapper.getWalletByUid(uid);
        if (pre_t_user_walletModel != null) return pre_t_user_walletModel.getWallet_address();

        Map<String, String> pairs = WalletService.getInstance().createNewWallet();
        String privateKey = pairs.get("privateKey");
        String publicKey = pairs.get("publicKey");
        String account = pairs.get("account");

        //encode privateKey
        String signedPrivateKey = WalletService.getInstance().encodePK(privateKey, uid);

        T_User_walletModel t_user_walletModel = new T_User_walletModel();
        t_user_walletModel.setUser_id(uid);
        t_user_walletModel.setPrivate_key(signedPrivateKey);
        t_user_walletModel.setPublic_key(publicKey);
        t_user_walletModel.setWallet_address(account);

        if (insert(t_user_walletModel) > 0) return "0x" + account;
        else throw new Exception("insert wallet error");
    }

    public JsonResult getWalletAddress(String uid) {
        String address = "";
        BigDecimal value = new BigDecimal(0);
        T_User_walletModel t_user_walletModel = t_User_walletMapper.getWalletByUid(uid);
        if (t_user_walletModel != null) address = "0x" + t_user_walletModel.getWallet_address();
        List<WalletTokenResp> resps = t_User_mytokenMapper.getUserAllToken(uid);
        for (WalletTokenResp resp: resps) {
            value.add(resp.getUsdtvalue());
        }

        HashMap<String, Object> rst = new HashMap<String, Object>();
        rst.put("address", address);
        rst.put("balance", value);
        return new JsonResult(rst);
    }

    /**
     * 提现申请
     */
    @Transactional(rollbackFor = {
            Exception.class}, isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
    public JsonResult withdraw(AccountWithdrawReq req, String uid, HttpServletRequest request) throws Exception {
        if (!com.bc.bcapi.utils.Ognl.isNotEmpty(req.getOutside_address())) {
            return new JsonResult(HttpReturnRnums.ParaError);
        }
        //验证钱包余额
        DBContextHolder.setDbType(DBContextDbType.Master.name());

        BigDecimal amount = req.getWithdraw_account();
        if (amount.compareTo(BigDecimal.ZERO) <= 0)
            return new JsonResult(HttpReturnRnums.ParaError);
        BigInteger gasPrice = Util.estimateGasPrice();
        String contract_id = req.getContract_id();
        T_Erc_contractModel contractModel = t_erc_contractMapper.selectContractByContractID(contract_id);
        if (contractModel == null) throw new Exception("not valid contract");
        BigInteger gasLimit = contractModel.getContract_id().equals(Util.getETHID()) ? Util.getETHGASLIMIT() : Util.getCONTRACTGASLIMIT();
        BigDecimal gas = Util.Wei2ETH(gasPrice.multiply(gasLimit));

        HashMap<String, Object> selectMap = new HashMap<>();
        selectMap.put("user_id", uid);
        selectMap.put("contract_id", contract_id);
        T_Wallet_infoModel contractWalletModel = t_wallet_infoMapper.selectForUpdate(selectMap);

        if (contractWalletModel == null) throw new Exception("not exist wallet");
        if (contractWalletModel.getAmount().compareTo(amount) < 0) throw new Exception("not enough token");

        T_Wallet_infoModel ethWalletModel = null;
        if (!contractModel.getContract_id().equals(Util.getETHID())) {
            selectMap.put("user_id", uid);
            selectMap.put("contract_id", Util.getETHID());
            ethWalletModel = t_wallet_infoMapper.selectForUpdate(selectMap);
            if (ethWalletModel == null) throw new Exception("not exist eth wallet");
            if (ethWalletModel.getAmount().compareTo(gas) < 0) throw new Exception("not enough gas");
        }

        //计算手续费 0.3%
        BigDecimal commission_rate = new BigDecimal(0.003).setScale(3, BigDecimal.ROUND_DOWN);
        int digit = contractModel.getDigit();
        if (digit > 8) digit = 8;
        BigDecimal commission = amount.multiply(commission_rate).setScale(digit, BigDecimal.ROUND_UP);
        BigDecimal actual_value = contractModel.getContract_id().equals(Util.getETHID()) ? amount.subtract(gas).subtract(commission) : amount.subtract(commission);

        if (actual_value.compareTo(BigDecimal.ZERO) <= 0)
            throw new Exception("not valid withdraw request");

        HashMap<String, Object> updateMap = new HashMap<>();
        updateMap.put("id", contractWalletModel.getId());
        updateMap.put("withdraw_account", amount);
        if (t_wallet_infoMapper.freeze(updateMap) <= 0)
            throw new Exception("withdraw freeze contract wallet error");

        if (!contractModel.getContract_id().equals(Util.getETHID())) {
            updateMap.put("id", ethWalletModel.getId());
            updateMap.put("withdraw_account", gas);
            if (t_wallet_infoMapper.freeze(updateMap) <= 0)
                throw new Exception("withdraw freeze gas wallet error");
        }
        String wid = t_sequenceService.genSequence(SeqNameEnum.WITHDRAW_ID);

        T_User_withdrawModel t_user_withdrawModel = new T_User_withdrawModel();
        t_user_withdrawModel.setWithdraw_id(wid);
        t_user_withdrawModel.setUser_id(uid);
        t_user_withdrawModel.setWithdraw_account(amount);
        t_user_withdrawModel.setToken_name(contractModel.getContract_id());
        t_user_withdrawModel.setActual_account(actual_value);
        t_user_withdrawModel.setGas_account(gas);
        t_user_withdrawModel.setService_token(contractModel.getContract_id());
        t_user_withdrawModel.setService_proportion(commission_rate);
        t_user_withdrawModel.setService_charge(commission);
        t_user_withdrawModel.setOutside_address(req.getOutside_address());
        if (t_user_withdrawMapper.insert(t_user_withdrawModel) <= 0) throw new Exception("insert into wallet error");

        String languageCookie = CookieUtils.getCookie(request, "local");
        String language = "CH";
        if (!StringUtils.isEmpty(languageCookie) && MultiLanguage.acceptLanguages.containsKey(languageCookie))
            language = MultiLanguage.acceptLanguages.get(languageCookie);
        String msg = MultiLanguage.withdrawApplyTip.get(language);
        return new JsonResult(msg);
    }

}